/*
 *   This File is Part of CuteOgre Game Engine
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *	Copyright 2010 Alexandre Pires ( c.alexandre.pires@gmail.com )
 *  Created on: 2010/08/22
 */

#include "VideoPlayer.h"
#include "ConfigManager.h"
#include "RenderManager.h"
#include "SoundManager.h"

#include "OGRE/OgreHardwarePixelBuffer.h"
#include "OGRE/OgreTextureManager.h"
#include "OGRE/OgreMaterialManager.h"
#include "OGRE/OgreResourceManager.h"

VideoPlayer::VideoPlayer ( QObject* rParent ) :
	QObject ( rParent ) {
	mVideo.clear ();
	mPlaying = false;
	mOverlay = 0;
	mFrameContainer = 0;
	mVideoFrame.setNull ();

	QString str = ConfigManager::readString ( "VideosPath", "Data/Videos;" );

	if ( str.endsWith(';') )
		str.chop(1);

	QStringList urls = str.split ( ';' );

	Ogre::ResourceGroupManager* mngr =
			&Ogre::ResourceGroupManager::getSingleton ();

	foreach ( QString url, urls )
			mngr->addResourceLocation ( url.toStdString(), "FileSystem", "Videos" );
}

VideoPlayer::~VideoPlayer () {

	if ( mPlaying )
		releaseCurrentVideo ();

	if ( mOverlay )
		Ogre::OverlayManager::getSingleton ().destroy ( mOverlay );

	if ( mFrameContainer )
		Ogre::OverlayManager::getSingleton ().destroyOverlayElement (
				mFrameContainer );

	mOverlay = 0;
	mFrameContainer = 0;
	mVideo.clear ();
	mVideoFrame.setNull ();
}

VideoPlayer::VideoPlayerState VideoPlayer::update () {

	mMutex.lock ();
	VideoPlayerState currentState = mCurrentState;
	mMutex.unlock ();

	if ( currentState == VPS_PLAYING ) {
		RenderManager::getSingleton ().update ();
		mSound->update ();
	}

	return currentState;
}

void VideoPlayer::playVideo ( OggFilePtr rVideo ) {

	if ( rVideo.isNull () )
		return;

	if ( rVideo->getTheoraStream ().isNull () )
		return;

	if ( mPlaying )
		releaseCurrentVideo ();

	if ( mOverlay == 0 ) {
		mOverlay = Ogre::OverlayManager::getSingleton ().create (
				"VideoFrameOverlay" );

		mFrameContainer
				= static_cast<Ogre::OverlayContainer*> ( Ogre::OverlayManager::getSingleton ().createOverlayElement (
						"Panel", "VideoFrameContainer" ) );

		mFrameContainer->setMetricsMode ( Ogre::GMM_PIXELS );
		mFrameContainer->setPosition ( 0, 0 );

		int width = ConfigManager::readInt ( "width", 640 );
		int height = ConfigManager::readInt ( "height", 480 );
		mFrameContainer->setDimensions ( width, height );

		mFrameContainer->setColour ( Ogre::ColourValue::Blue );
		mOverlay->add2D ( mFrameContainer );
		mFrameContainer->show ();
	}

	mMutex.lock ();
	mCurrentState = VPS_LOADING;
	mMutex.unlock ();

	int width = rVideo->getTheoraStream ()->getFrameWidth ();
	int height = rVideo->getTheoraStream ()->getFrameHeight ();

	Ogre::TextureManager* textureManager =
			Ogre::TextureManager::getSingletonPtr ();

	mVideoTexture = textureManager->getByName ( "VideoTexture", "Videos" );

	if ( mVideoTexture.isNull () ) {
		mVideoTexture = textureManager->createManual ( "VideoTexture",
				"Videos", Ogre::TEX_TYPE_2D, width, height, 1, 0,
				Ogre::PF_X8R8G8B8, Ogre::TU_DYNAMIC_WRITE_ONLY );
	} else {
		mVideoTexture->freeInternalResources ();
		mVideoTexture->setWidth ( width );
		mVideoTexture->setHeight ( height );
		mVideoTexture->createInternalResources ();
	}

	if ( mVideoFrame.isNull () ) {
		mVideoFrame = Ogre::MaterialManager::getSingleton ().create (
				"VideoFrame", "Videos" );
		mVideoFrame->getTechnique ( 0 )->getPass ( 0 )->createTextureUnitState (
				"VideoTexture" );
		mVideoFrame->getTechnique ( 0 )->getPass ( 0 )->setDepthCheckEnabled (
				false );
		mVideoFrame->getTechnique ( 0 )->getPass ( 0 )->setDepthWriteEnabled (
				false );
		mVideoFrame->getTechnique ( 0 )->getPass ( 0 )->setLightingEnabled (
				false );
	} else {
		mVideoFrame->reload ();
	}

	mFrameContainer->setMaterialName ( "VideoFrame" );

	if ( RenderManager::getSingleton ().getCurrentMode ()
			!= RenderManager::ModeVideoPlayback )
		RenderManager::getSingleton ().switchToMode (
				RenderManager::ModeVideoPlayback );

	mVideo = rVideo;

	RenderManager::getSingleton ().getRoot ()->addFrameListener ( this );

	mSound = SoundSourcePtr ( new SoundSource ( 10, rVideo ) );

	mMutex.lock ();
	mTime.start ();
	mCurrentState = VPS_PLAYING;
	mPlaying = true;
	mOverlay->show ();
	mMutex.unlock ();

	mSound->startPlayback ();
}

void VideoPlayer::releaseCurrentVideo () {

	mOverlay->hide ();
	RenderManager::getSingleton ().getRoot ()->removeFrameListener ( this );

	Ogre::TextureManager* textureManager =
			Ogre::TextureManager::getSingletonPtr ();

	Ogre::TexturePtr frame = textureManager->getByName ( "VideoTexture",
			"Videos" );

	if ( !frame.isNull () ) {
		mVideoTexture->freeInternalResources ();
		mVideoTexture->unload ();
	}

	mPlaying = false;
	mCurrentState = VPS_NOVIDEO;
	mSound->stopPlayback ();
	mSound.clear ();
	mVideoTexture.setNull ();
	mVideo.clear ();
}

bool VideoPlayer::frameStarted ( const Ogre::FrameEvent& evt ) {

	(void) evt;

	mMutex.lock ();
	bool playing = mPlaying;
	VideoPlayerState state = mCurrentState;
	mMutex.unlock ();

	if ( !playing || state != VPS_PLAYING )
		return false;

	float currentTime = float ( mTime.elapsed () ) / 1000.f;
	float videoTime = mVideo->getTheoraStream ()->getLastFrameTime ();

	if ( currentTime > videoTime ) {
		Ogre::HardwarePixelBufferSharedPtr buffer = mVideoTexture->getBuffer ();
		buffer->lock ( Ogre::HardwareBuffer::HBL_DISCARD );
		Ogre::PixelBox frameBuffer = buffer->getCurrentLock ();
		playing = mVideo->getTheoraStream ()->read ( &frameBuffer );
		buffer->unlock ();

		mMutex.lock ();
		mPlaying = playing;

		if ( !playing ) {
			mCurrentState = VPS_NOVIDEO;
		}
		mMutex.unlock ();
	} else {
		return false;
	}

	return true;
}

bool VideoPlayer::frameRenderingQueued ( const Ogre::FrameEvent& evt ) {
	(void) evt;
	return true;
}

bool VideoPlayer::frameEnded ( const Ogre::FrameEvent& evt ) {
	(void) evt;
	return true;
}
